package cn.plem.ml;

import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ResourceBundle;

import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.TermCriteria;
import org.opencv.ml.SVM;
import org.opencv.ml.TrainData;

public class Svm {

	static {
		nu.pattern.OpenCV.loadShared();
		System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
	}

	/**
	 * svm.properties
	 * 
	  # cv.ml.SVM.Types 
	  # C_SVC = 100, NU_SVC = 101,ONE_CLASS = 102,EPS_SVR = 103,NU_SVR = 104 
	  cv.ml.SVM.Types=100
	  
	  # cv.ml.SVM.KernelTypes 
	  # CUSTOM = -1, LINEAR = 0,POLY = 1,RBF = 2, SIGMOID = 3,CHI2 = 4,INTER = 5 
	  cv.ml.SVM.KernelTypes=0
	  
	  # cv.ml.SampleTypes 
	  # ROW_SAMPLE = 0,COL_SAMPLE = 1 
	  cv.ml.SampleTypes=0
	  
	  termCriteria.maxCount=100 
	  termCriteria.epsilon=1e-7
	 */
	ResourceBundle rb = ResourceBundle.getBundle("ml");
	TrainData trainData;

	SVM svm;

	public static void main(String[] args) throws IOException {
		CsvTrain csvTrain = new CsvTrain("iris");
		TrainData trainData = csvTrain.trainData(Paths.get("iris.data"));
		Path xml = csvTrain.path("iris.svm.xml");
		Svm svm = new Svm(trainData, xml);
		float[] d = { 5.7f, 2.8f, 4.1f, 1.3f };
		System.out.println("predict:" + svm.predict(d));
	}

	public Svm(TrainData trainData, Path xml) throws IOException {
		this.trainData = trainData;
		if (xml.toFile().exists()) {
			svm = SVM.load(xml.toString());
		} else {
			svm = SVM.create();
			train(trainData);
			svm.save(xml.toString());
		}
	}

	public boolean train(TrainData trainData) {
		int type = rb.containsKey("cv.ml.SVM.Types") ? Integer.parseInt(rb.getString("cv.ml.SVM.Types")) : SVM.C_SVC;
		int kernel = rb.containsKey("cv.ml.SVM.KernelTypes") ? Integer.parseInt(rb.getString("cv.ml.SVM.KernelTypes"))
				: SVM.LINEAR;
		int maxCount = rb.containsKey("termCriteria.maxCount") ? Integer.parseInt(rb.getString("termCriteria.maxCount"))
				: 100;
		double epsilon = rb.containsKey("termCriteria.epsilon")
				? Double.parseDouble(rb.getString("termCriteria.epsilon"))
				: 1e-3;
		
		svm.setType(type);
		svm.setKernel(kernel);
		svm.setTermCriteria(new TermCriteria(TermCriteria.MAX_ITER, maxCount, epsilon));

		return svm.train(trainData);
	}

	/**
	 * 
	 * @param data = { 5.7f, 2.8f, 4.1f, 1.3f };
	 * @return
	 */
	public int predict(float[] data) {
		int cols = trainData.getSamples().cols();
		Mat sampleMat = new Mat(1, cols, CvType.CV_32FC1);
		sampleMat.put(0, 0, data);
		return (int)svm.predict(sampleMat);
	}

}
